9.3.1 通过通道实现同步
也许你已经猜到了,通道非常适用于对两个goroutine进行同步,当一个goroutine需要依赖另一个goroutine时,更是如此。事不宜迟,让我们马上来看看代码清单9-7所示的程序:这个程序沿用了上一节展示过的例子,唯一的不同在于,这次的程序使用了通道而不是等待组来对goroutine进行同步。
代码清单9-7 使用通道同步goroutine
package main
import "fmt"
import "time"
func printNumbers2(w chan bool) {
for i := 0; i < 10; i++ {
time.Sleep(1 * time.Microsecond)
fmt.Printf("%d ", i)
}
w <- true ❶
} ❶
func printLetters2(w chan bool) { ❶
for i := 'A'; i < 'A'+10; i++ { ❶
time.Sleep(1 * time.Microsecond) ❶
fmt.Printf("%c ", i) ❶
} ❶
w <- true ❶
}
func main() {
w1, w2 := make(chan bool), make(chan bool)
go printNumbers2(w1)
go printLetters2(w2)
<-w1 ❷
<-w2 ❷
}
❶ 把一个布尔值放入通道,以便解除主程序的阻塞状态
❷ 主程序将一直阻塞,直到通道里面出现可弹出的值为止
先来看看这个程序中的 main
函数。它首先创建了 w1
和 w2
这两个 bool
类型的通道,接着以goroutine方式运行了 printNumbers2
函数和 printLetters2
函数,并将两个通道分别传给了这两个函数。在启动两个goroutine之后, main
函数将会尝试从通道 w1
中移除一个值,但由于通道 w1
当时并没有包含任何值,所以 main
函数将会在此处阻塞。当 printNumbers2
即将执行完毕,并将一个 true
值放入通道 w1
之后, main
函数的阻塞状态才会被解除,并继续尝试从第二个通道 w2
中弹出一个值。跟之前一样,在 printLetters2
执行完毕并将 true
值放入通道 w2
之前, main
函数将一直阻塞,直到它成功取得了 w2
通道中的 true
值之后,阻塞才会解除,然后 main
函数才会顺利退出。
需要注意的是,因为我们只是想要在goroutine执行完毕之后解除对 main
函数的阻塞,而不是真正地想要使用通道中存储的值,所以程序在从通道 w1
和 w2
里面取出值之后并没有使用这些值。
代码清单9-7展示的是一个非常简单的例子,这个例子中的程序使用通道只是为了对多个goroutine进行同步,但这些goroutine之间并没有通信。不过在接下来的一节,我们就会看到一个在多个goroutine之间传递消息的例子。